package panda.server.util.datamine;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import panda.client.enumerations.CardRarity;
import panda.client.enumerations.MagicSet;
import panda.server.data.CardData;
import panda.server.data.MagicCardDB;
import panda.server.util.sealedproduct.TextProductGenerator;

/**
 * Mines price data from magiccards.info using its partner.tcgplayer.com's script page.
 * Still works as of Tuesday April 13th, 2010 after magiccard.info's update from beta.
 * 
 * @author zane.tian@gmail.com Saturday October 17th, 2009 9:53pm
 */
public class TcgPriceMiner {

	public static void main(String[] args) {
		List<CardData> targets = new ArrayList<CardData>();
		List<CardData> standardDB = MagicCardDB.getAllStandardLegalCards();

		try {
			File targetFile = new File("src/panda/server/util/datamine/cards-that-need-prices.txt");
			Scanner scanner = new Scanner(targetFile);
			String cardLine = "not initialized";
			CardData target = null; /* must be initialized or won't compile */
			boolean cardFound;

			while(scanner.hasNext()) {
				cardLine = scanner.nextLine();

				/* skip blank lines and comment lines */
				if(cardLine.equals("") || cardLine.substring(0, 2).equals("//")) {
					continue;
				}

				cardFound = false;
				/* linear search for the card name in all of standard :wince: */
				for(CardData card : standardDB) {
					if(cardLine.toLowerCase().equals(card.getName().toLowerCase())) {
						target = card;
						cardFound = true;
						break;
					}
				}

				if(cardFound) {
					targets.add(target);
				} else {
					throw new IllegalStateException(cardLine + " was not found in the 'DB'. Check spelling?");
				}
			}
		} catch(Exception ex) {
			ex.printStackTrace();
		}

		DecimalFormat df = new DecimalFormat();
		df.setMinimumFractionDigits(2);

		try {
			

			double accum = 0.0;
			targets = MagicCardDB.getCardsFromSet(MagicSet.EVENTIDE);
			for(CardData card : targets) {
				if(card.getRarity().equals(CardRarity.RARE) || card.getRarity().equals(CardRarity.MYTHIC)) {
					accum += getCardPrice(card, PriceLevel.med);
					// dualPrint(p, pad(card.getName(), 30));
					// dualPrint(p, df.format(getCardPrice(card, PriceLevel.low) + "\n"));
					
					System.out.println(pad(card.getName(), 30) + df.format(getCardPrice(card, PriceLevel.med)));
				}
			}

			System.out.println("total value: " + accum);

		} catch(Exception e) {
			System.err.print("Error writing to file");
		}
	}
	
	public void printPricesTxt() {
		/* /Users/kyruru/Dropbox/prices.txt */
//		FileOutputStream out;
//		PrintStream p;
//		
//		out = new FileOutputStream("C:/Users/kyruru/Documents/My Dropbox/prices.txt");
//		p = new PrintStream(out);
//
//		for(CardData card : targets) {
//			dualPrint(p, pad(card.getName(), 30));
//			dualPrint(p, pad(df.format(getCardPrice(card, PriceLevel.low)), 7));
//			dualPrint(p, pad(df.format(getCardPrice(card, PriceLevel.med)), 7));
//			dualPrint(p, pad(df.format(getCardPrice(card, PriceLevel.high)), 7));
//
//			dualPrint(p, "\n");
//		}
//
//		p.close();
	}

	/**
	 * Prints to 2 streams at once.
	 */
//	public static void dualPrint(PrintStream p, String text) {
//		p.print(text);
//		System.out.print(text);
//	}

	private enum PriceLevel {
		low("L:"), med("M:"), high("H:");

		private PriceLevel(String prefix) {
			this.precursor = prefix;
		}

		private String precursor;

		public String getPrecursor() {
			return precursor;
		}
	}

	/**
	 * Scrapes price data from partner.tcgplayer.com's js "insertion" API. As of Fall '09, Magiccard.info gets its
	 * pricing information from tcgplayer.com. If you try to "view page source" on a magiccards.info card page, you see
	 * the following script link: http://partner.tcgplayer.com/syn/synhighlow.ashx?pk=MAGCINFO&pi=zen-168 Note that "zen"
	 * is the card set abbreviation and "168" is the card ID within the set. This link directs to a script that uses
	 * document.write() to display the pricing information on a webpage. We can use regex to match "L:", "M:", and "H:"
	 * in the HTML with the price string ($nnn.nn) that follows immediately after each one to determine the low, medium,
	 * and high prices for the card. Note: Oct 18, 2009 - Some M10 scripts don't work. Almost all the ones at the end of
	 * a color are similar. This includes Zephyr Sprite, Wurm's Tooth, etc.
	 * 
	 * @param card A MagicCard: grab these from MagicCardDB.
	 * @param level Level.LOW, Level.MED, or Level.HIGH
	 * @return
	 */
	public static double getCardPrice(CardData card, PriceLevel level) {
		StringBuffer buffer = new StringBuffer();
		String tcgUrlRoot = "http://partner.tcgplayer.com/syn/synhighlow.ashx?pk=MAGCINFO&pi=";
		String amtRegex = "> \\$ (\\d{1,} [.] \\d{2}) <";
		URL link = null;
		double result = -1.0;

		try {
			/* http://partner.tcgplayer.com/syn/synhighlow.ashx?pk=MAGCINFO&pi=zen-168 */
			link = new URL(tcgUrlRoot + card.getMagicSet().getAbbrev() + "-" + card.getID());
			InputStream in = link.openStream();

			BufferedInputStream stream = new BufferedInputStream(in);
			Scanner scanner = new Scanner(stream, "UTF8");

			buffer = new StringBuffer();
			while(scanner.hasNext()) {
				String line = scanner.nextLine();
				buffer.append(line);
			}

			/* careful this is case sensitive because css styles in the url have l: m: h: */
			Pattern pattern = Pattern.compile(level.getPrecursor() + ".*?" + amtRegex, Pattern.COMMENTS);
			Matcher matcher = pattern.matcher(buffer);

			if(matcher.find()) {
				result = (double) Double.parseDouble(matcher.group(1));
			} else {
				System.err.println(card.getName() + " not found");
			}

			in.close();
		} catch(MalformedURLException e) {
			e.printStackTrace();
		} catch(IOException e) {
			e.printStackTrace();
		}

		return result;
	}

	/** same as getCardPrice(CardData, PriceLevel), except it returns an array of all the prices */
	public static double[] getCardPrice(String name, MagicSet set, int id) {
		StringBuffer buffer = new StringBuffer();
		String tcgUrlRoot = "http://partner.tcgplayer.com/syn/synhighlow.ashx?pk=MAGCINFO&pi=";
		String amtRegex = "> \\$ (\\d{1,} [.] \\d{2}) <";
		URL link = null;
		double[] result = { 0, 0, 0 };

		try {
			/* http://partner.tcgplayer.com/syn/synhighlow.ashx?pk=MAGCINFO&pi=zen-168 */
			link = new URL(tcgUrlRoot + set.getAbbrev() + "-" + id);
			InputStream in = link.openStream();

			BufferedInputStream stream = new BufferedInputStream(in);
			Scanner scanner = new Scanner(stream, "UTF8");

			buffer = new StringBuffer();
			while(scanner.hasNext()) {
				String line = scanner.nextLine();
				buffer.append(line);
			}

			/* careful this is case sensitive because css styles in the url have l: m: h: */
			int i = 0;
			for(PriceLevel level : PriceLevel.values()) {
				Pattern pattern = Pattern.compile(level.getPrecursor() + ".*?" + amtRegex, Pattern.COMMENTS);
				Matcher matcher = pattern.matcher(buffer);

				if(matcher.find()) {
					result[i] = (double) Double.parseDouble(matcher.group(1));
				} else {
					System.err.println(name + " not found");
				}

				i++;
			}

			in.close();
		} catch(MalformedURLException e) {
			e.printStackTrace();
		} catch(IOException e) {
			e.printStackTrace();
		}

		return result;
	}

	private static String pad(String text, int spaces) {
		return TextProductGenerator.pad(text, spaces);
	}
}